#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nleft = count; //剩余的字节数
    size_t nread;         //已经读取的字节数

    char *bufp = (char *)buf;
    //因为不能够保证一次能够读到多少字节，所以循环读取，直到读完
    while (nleft > 0)
    {
        if ((nread = read(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR) //被信号中断，不认为出错
            {
                continue;
            }
            else
                return -1;
        }
        else if (nread == 0)
        {
            return count - nleft; //已经读取的字节数
        }

        bufp += nread;
        nleft -= nread;
    }
    return count;
}
ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    size_t nwritten;

    char *bufp = (char *)buf;

    while (nleft > 0)
    {
        if ((nwritten = write(fd, bufp, nleft)) < 0)
        {
            if (errno == EINTR) //被信号中断，不认为出错
            {
                continue;
            }
            else
                return -1;
        }
        else if (nwritten == 0)
        {
            continue;
        }
        bufp += nwritten;
        nleft -= nwritten;
    }
    return count;
}
//从sockfd的数据到buf中，并没有挪走sockfd数据
ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
    while (1)
    {
        int ret = recv(sockfd, buf, len, MSG_PEEK);
        if (ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}
//读取一行数据，碰到\n就返回，maxline时一行中最多字符的个数
ssize_t readline(int sockfd, void *buf, size_t maxline)
{
    int ret;
    int nread;
    int nleft = maxline;
    char *bufp = (char *)buf;

    while (1)
    {
        //查看sockfd的数据
        ret = recv_peek(sockfd, bufp, nleft);
        if (ret < 0)
            return ret;
        else if (ret == 0)
            return ret;
        //判断查看数据是否\n
        nread = ret;
        int i;
        for (i = 0; i < nread; i++)
        {
            if (bufp[i] == '\n') //有\n 就读走
            {
                ret = readn(sockfd, bufp, i + 1);
                if (ret != i + 1)
                {
                    perror("readn");
                    exit(1);
                }
                return ret;
            }
        }
        // recv_peek的内容，没有包含\n的处理方式
        if (nread > nleft)
        {
            exit(1);
        }
        nleft -= nread;

        ret = readn(sockfd, bufp, nread);
        if (ret != nread)
        {
            perror("readn");
            exit(1);
        }
        bufp += nread;
    }
    return -1;
}
int main()
{
    int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket:");
        exit(1);
    }

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons(5188);

    if ((connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) < 0)
    {
        perror("connect:");
        exit(1);
    }

    fd_set rset; // 监听可读事件的文件描述符集合
    FD_ZERO(&rset);

    int nready; //可读事件就绪的文件描述符个数
    //这里逻辑处理是监听两个可读事件：1.stdin;2.sockfd;
    int fd_stdin = fileno(stdin);
    int maxfd;
    if (fd_stdin > sockfd)
        maxfd = fd_stdin;
    else
        maxfd = sockfd;

    char sendbuf[1024] = {0};
    char recvbuf[1024] = {0};

    while (1)
    {
        FD_SET(fd_stdin,&rset);
        FD_SET(sockfd,&rset);
        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
        if (nready < 0)
        {
            perror("select");
            exit(1);
        }
        if (nready == 0)
            continue;

        //检测的事件成功，看看是什么事件被检测成功

        if (FD_ISSET(sockfd, &rset)) //如果sockfd套接口可读事件产生（也就是客户端发送数据过来了)
        {
            int sz = readline(sockfd, recvbuf, sizeof(recvbuf));
            if (sz == -1)
            {
                perror("readline");
                exit(1);
            }
            if (sz == 0)
            {
                printf("peer colse \n");
                break;
            }
            fputs(recvbuf, stdout); //客户端打印服务端回射的数据
            memset(recvbuf, 0, sizeof(recvbuf));
        }
        //检测到键盘的数据
        if (FD_ISSET(fd_stdin, &rset))
        {
            printf("client send to serivce:");
            //客户端接收键盘数据放到sendbuf
            if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
                break;
            writen(sockfd, sendbuf, strlen(sendbuf)); //客户端的数据发送给服务端
            memset(sendbuf, 0, sizeof(sendbuf));
            printf("client send to serivce:");
        }
    }
    close(sockfd);

    return 0;
}